////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2011-2012 www.iflytek.com. All Rights Reserved.
//  This software for customer relationship management system, developed by Noo team.
//  Software code and design for the team, copy rights reserved.
////////////////////////////////////////////////////////////////////////////////

package com.iflytek.ci.cums.service.impl;

import java.io.Serializable;
import java.util.Date;
import java.util.List;

import com.iflytek.ci.cums.domain.Account;
import com.iflytek.ci.cums.domain.Permissions;
import com.iflytek.ci.cums.domain.Role;
import com.iflytek.ci.cums.service.AccountService;
import com.iflytek.ci.cums.service.RoleService;
import com.iflytek.ci.cums.service.SystemConfigurationService;
import com.iflytek.ci.main.domain.SystemConfiguration;
import com.iflytek.framework.ldap.Authentication;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

/**
 * <p>
 * 权限认证.
 * </p>
 *
 * @author poplar.yfyang
 * @version 1.0 2012-10-25 9:56 PM
 * @since JDK 1.5
 */
public class ShiroDbRealm extends AuthorizingRealm {
	/** 用户业务管理服务 */
	protected final AccountService accountService;
	/** role service */
	private final RoleService roleService;
	/** Ldap 域帐号 */
	private final Authentication authentication;
	/** 是否经过域服务器登录 */
	private final boolean ldap_login;

	/**
	 * 构建一个认证服务，由用户帐号服务来控制
	 *
	 * @param accountService 用户信息服务
	 * @param authentication 域帐号登录服务
	 */
	public ShiroDbRealm(AccountService accountService, RoleService roleService,
						Authentication authentication, SystemConfigurationService systemConfigurationService) {
		super();
		//set authentication token implementation class.
		setAuthenticationTokenClass(UsernamePasswordToken.class);
		this.accountService = accountService;
		this.authentication = authentication;
		this.roleService = roleService;
		SystemConfiguration configuration = systemConfigurationService.system_config();

		this.ldap_login = (configuration != null) && configuration.isLdap();
	}

	/**
	 * 授权查询回调函数，进行鉴定权限但是缓存中无用户的授权信息时调用
	 *
	 * @param principals 授权信息，包含用户信息
	 * @return 认证信息
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		final ShiroUser shiroUser = (ShiroUser) principals.getPrimaryPrincipal();
		final Account account = accountService.accountWithName(shiroUser.login);

		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
		List<Role> roleList = roleService.user_role(account.getId());
		for (Role role : roleList) {
			//基于Role的权限信息
			info.addRole(role.getName());
			for (Permissions permissions : role.getPermissionsList()) {
				// 基于Permission的权限信息
				info.addStringPermission(permissions.getPermission());
			}
		}
		return info;
	}

	/**
	 * 认证用户回调函数，登录时使用
	 *
	 * @param token the user login token.
	 * @return authentication information
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		final UsernamePasswordToken up_token = (UsernamePasswordToken) token;
		Account account = accountService.accountWithName(up_token.getUsername());
		if (account == null) {
			if (ldap_login) {
				//首先从域服务器登录，获取用户信息
				boolean login_on_ldap = authentication.authentication(up_token.getUsername(), String.valueOf(up_token.getPassword()));
				if (login_on_ldap) {
					//域服务器登录成功，但是cdms系统中不存在这个用户
					account = new Account();
					account.setLogin(up_token.getUsername());
					account.setName(up_token.getUsername());
					account.setPassword(EncryptionPassword.encryption(String.valueOf(up_token.getPassword())));
					account.setMail(up_token.getUsername() + "@iflytek.com");
					account.setLastLogin(new Date());
					accountService.addAccount(account);
					return new SimpleAuthenticationInfo(new ShiroUser(account.getLogin(), account.getName(), account.getId(), account.getMail()), account.getPassword(), getName());
				}
			}
		} else {
			//不从域服务器登录
			account.setLastLogin(new Date());
			accountService.update_time(account);
			return new SimpleAuthenticationInfo(new ShiroUser(account.getLogin(), account.getName(), account.getId(), account.getMail()), account.getPassword(), getName());
		}

		return null;
	}

	/** 自定义Authentication对象，使得Subject除了携带用户的登录名外还可以携带更多信息. */
	public static class ShiroUser implements Serializable {
		/** serial version */
		private static final long serialVersionUID = -1373760761780840081L;
		/** login name */
		private final String login;
		/** account name */
		private final String name;
		/** account id */
		private final long id;
		/** 电子邮件 */
		private final String email;

		/**
		 * create new Shiro User
		 *
		 * @param login login name
		 * @param name  account name
		 * @param id    account id
		 * @param email email
		 */
		public ShiroUser(String login, String name, long id, String email) {
			this.login = login;
			this.name = name;
			this.id = id;
			this.email = email;
		}

		/**
		 * account name
		 *
		 * @return account name
		 */
		public String getName() {
			return name;
		}

		/**
		 * account id
		 *
		 * @return account's id
		 */
		public long getId() {
			return id;
		}

		/**
		 * get user login
		 *
		 * @return user'login
		 */
		public String getLogin() {
			return login;
		}

		/**
		 * get user email
		 *
		 * @return email
		 */
		public String getEmail() {
			return email;
		}

		/** 本函数输出将作为默认的<shiro:principal/>输出. */
		@Override
		public String toString() {
			return name;
		}

		/** 重载equals,只计算login; */
		@Override
		public int hashCode() {
			return HashCodeBuilder.reflectionHashCode(this, "login");
		}

		/**
		 * 重载equals,只比较login
		 *
		 * @param obj 比较的对象
		 */
		@Override
		public boolean equals(Object obj) {
			return EqualsBuilder.reflectionEquals(this, obj, "login");
		}
	}
}
